package com.xwj.util.RedisUtils.aop;

import com.xwj.Enums.CacheTypeEnum;
import com.xwj.service.redis.RedisService;
import com.xwj.util.RedisUtils.annotations.RedisAddOrUpdate;
import com.xwj.util.RedisUtils.annotations.RedisCacheDelete;
import com.xwj.util.RedisUtils.annotations.RedisCacheable;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 自定义缓存注解切面
 */
@Component
@Aspect
public class RedisCacheAop {

    @Autowired
    private RedisService redisService;

    /**
     * 切入
     */
    @Pointcut("@annotation(com.xwj.util.RedisUtils.annotations.RedisCacheable)")
    public void getRedisCacheable() {
    }

    @Pointcut("@annotation(com.xwj.util.RedisUtils.annotations.RedisAddOrUpdate)")
    public void getRedisAddOrUpdate() {
    }

    @Pointcut("@annotation(com.xwj.util.RedisUtils.annotations.RedisCacheDelete)")
    public void getRedisCacheDelete() {
    }


    /**
     * 在所有标注@RedisCacheable的地方切入
     * 读缓存
     *
     * @param joinPoint
     * @return
     */
    @Around("getRedisCacheable()")
    public Object beforeExecRedisCacheable(ProceedingJoinPoint joinPoint) {
        String[] strValue = StringUtils.split(getRedisCacheableKey(joinPoint), "_");
        String key = strValue[0];
        String type = strValue[1];
        long expireTime = Long.parseLong(strValue[2]);
        Object objectFromRedis = redisService.get(key);
        if (objectFromRedis != null)
            return objectFromRedis;
        Object object = null;
        try {
            object = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        if (type.equals(CacheTypeEnum.Type_String.getType()))
            redisService.set(key, object, expireTime);
        return object;
    }

    /**
     * 添加或更新缓存
     *
     * @param joinPoint
     * @return
     */
    @Around("getRedisAddOrUpdate()")
    public void beforeExecRedisAddOrUpdate(ProceedingJoinPoint joinPoint) {
        try {
            String[] strValue = StringUtils.split(getRedisAddOrUpdateKey(joinPoint), "_");
            String key = strValue[0];
            String type = strValue[1];
            long expireTime = Long.parseLong(strValue[2]);
            Object object = joinPoint.proceed();
            if (type.equals(CacheTypeEnum.Type_String.getType()))
                redisService.set(key, object, expireTime);
            else if (type.equals(CacheTypeEnum.Type_List)) {
                if (redisService.exists(key))
                    redisService.lrem(key, 1, object);
                redisService.lPush(key, object);
            } else if (type.equals(CacheTypeEnum.Type_Set)) {
                if (redisService.exists(key))
                    redisService.removeMember(key, object);
                redisService.set(key, object, expireTime);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    /**
     * 删除缓存
     * @param joinPoint
     * @return
     */
    @Around("getRedisCacheDelete()")
    public void beforeExecRedisCacheDelete(ProceedingJoinPoint joinPoint) {
        try {
            String[] strValue = StringUtils.split(getRedisCacheDeleteKey(joinPoint), "_");
            String key = strValue[0];
            String type = strValue[1];
            Object object = joinPoint.proceed();
            if (type.equals(CacheTypeEnum.Type_String.getType()))
                redisService.remove(key);
            else if (type.equals(CacheTypeEnum.Type_List)) {
                if (redisService.exists(key))
                    redisService.lrem(key, 1, object);
            } else if (type.equals(CacheTypeEnum.Type_Set)) {
                if (redisService.exists(key))
                    redisService.removeMember(key, object);
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }


    @SuppressWarnings("unused")
    private String getRedisCacheableKey(ProceedingJoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        StringBuilder builder = new StringBuilder();
        builder.append(method.getAnnotation(RedisCacheable.class).key());
        builder.append("_");
        builder.append(method.getAnnotation(RedisCacheable.class).type());
        builder.append("_");
        builder.append(method.getAnnotation(RedisCacheable.class).expireTime());
        return builder.toString();
    }

    @SuppressWarnings("unused")
    private String getRedisAddOrUpdateKey(ProceedingJoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        StringBuilder builder = new StringBuilder();
        builder.append(method.getAnnotation(RedisAddOrUpdate.class).key());
        builder.append("_");
        builder.append(method.getAnnotation(RedisAddOrUpdate.class).type());
        builder.append("_");
        builder.append(method.getAnnotation(RedisAddOrUpdate.class).expireTime());
        return builder.toString();
    }

    @SuppressWarnings("unused")
    private String getRedisCacheDeleteKey(ProceedingJoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        StringBuilder builder = new StringBuilder();
        builder.append(method.getAnnotation(RedisCacheDelete.class).key());
        builder.append("_");
        builder.append(method.getAnnotation(RedisCacheDelete.class).type());
        return builder.toString();
    }
}
